/*
 * FolderArgumentSource.cpp
 *
 *  Created on: 26 juin 2011
 *      Author: Pierrick Caillon <pierrick@megami.fr>
 */

#include "FolderArgumentSource.h"
#include "CommandLine.h"
#include <unistd.h>
#include <fstream>
#include "SourceBuilder.h"
#include "CombineSourceBuilder.h"
#include <dirent.h>

namespace po = boost::program_options;
namespace nbt = NamedBinaryTag;
po::options_description folderOptions("Folder source");
po::options_description &initFolderParams();
CommandLine::Register regIDP(initFolderParams, CommandLine::OPT_CLI_HELP);

SourceBuilder::Registrer regDAS(FolderArgumentSource::get_Instance,
		FolderArgumentSource::is_Valid);

class FolderEnumerator: public CombineSourceBuilder::iterator
{
private:
	enum
	{
		STATE_START, STATE_READING, STATE_END, STATE_ERROR
	} state;
	DIR *directory;
	std::string path;
	std::ifstream input;
public:
	FolderEnumerator(std::string path);
	~FolderEnumerator();
	bool next();
	std::istream* current();
	bool valid();
};

FolderArgumentSource::FolderArgumentSource()
{
}

FolderArgumentSource::~FolderArgumentSource()
{
}

ISource *FolderArgumentSource::get_Instance()
{
	return new FolderArgumentSource();
}

NamedBinaryTag::DataFile FolderArgumentSource::GetDataFile()
{
	std::string value =
			CommandLine::get_Item("input-folder").as<std::string> ();
	NamedBinaryTag::DataFile nbtFile;
	CombineSourceBuilder builder;
	FolderEnumerator enumerator(value);
	std::list<nbt::DataFile*> dataFiles(builder.LoadFiles(enumerator));
	CombineSourceBuilder::ListCleaner cleaner(dataFiles);
	if (dataFiles.size() > 0)
	{
		Rect bounds(builder.ComputeBounds(dataFiles));
		char *image = builder.CreateImage(dataFiles, bounds);
		nbtFile.Init();
		nbtFile.get_RootTag()->Add(builder.MakeDataTag(dataFiles, image, bounds));
	}
	return nbtFile;
}

bool FolderArgumentSource::is_Valid()
{
	const po::variable_value &valueVar = CommandLine::get_Item("input-folder");
	if (valueVar.empty())
		return false;
	std::string value = valueVar.as<std::string> ();
	return FolderEnumerator(value).valid();
}

po::options_description &initFolderParams()
{
	folderOptions.add_options()("input-folder,F", po::value<std::string>(),
			"input Minecraft map items folder.");
	return folderOptions;
}


bool FolderEnumerator::next()
{
	switch (state)
	{
	case STATE_READING:
		input.close();
	case STATE_START:
		state = STATE_ERROR;
		while (true)
		{
			dirent64 *entry = readdir64(directory);
			if (entry == NULL)
			{
				state = STATE_END;
				break;
			}
			size_t len = strlen(entry->d_name);
			if (strncasecmp(entry->d_name, "map_", 4) != 0)
				continue;
			if (strncasecmp(entry->d_name + len - 4, ".dat", 4) != 0)
				continue;
			std::string file = path + entry->d_name;
			input.open(file.c_str(), std::ios::in | std::ios::binary);
			if (input.fail())
			{
				state = STATE_ERROR;
				break;
			}
			state = STATE_READING;
			break;
		}
		return state == STATE_READING;
	case STATE_END:
	case STATE_ERROR:
		return false;
	}
	return false;
}

std::istream *FolderEnumerator::current()
{
	if (state == STATE_READING)
		return &input;
	return NULL;
}

FolderEnumerator::~FolderEnumerator()
{
	if (directory != NULL)
		closedir(directory);
	directory = NULL;
}

FolderEnumerator::FolderEnumerator(std::string path)
{
	this->path = ((path.length() > 1) && (path[path.length() - 1] != '/')) ? (path + '/') : (path);
	directory = opendir(this->path.c_str());
	if (directory != NULL)
		state = STATE_START;
	else
		state = STATE_ERROR;
}

bool FolderEnumerator::valid()
{
	return directory != NULL;
}
